<?php


namespace common\service;

use common\model\EntityModel;
use common\model\PlanModel;
use common\model\TaskModel;
use common\model\TaskWorkOrderModel;
use common\model\UserModel;
use common\service\step_category_month_report\CompleteFilmReportCount;
use DateInterval;
use think\Exception;
use support\ErrorCode;
use support\SingletonTrait;
use common\exception\LogicException;
use common\service\step_category_month_report\EntityReportCount;
use common\service\step_category_month_report\EpisodeReportCount;
use common\service\step_category_month_report\ShotMotionReportCount;
use common\service\step_category_month_report\MotionCaptureReportCount;


class StepCategoryMonthReportService
{
    use SingletonTrait;

    /**
     * 获得处理策略对象
     * @param $stepCategoryIdList
     * @return EpisodeReportCount
     */
    public function getHandleObject($stepCategoryIdList)
    {
        $stepCategoryList = StepCategoryService::getInstance()->getList(['id' => ['IN', implode(',', $stepCategoryIdList)]], '*');
        if (empty($stepCategoryList)) {
            throw new LogicException('step_category not found', ErrorCode::STEP_CATEGORY_NOT_FOUND);
        }
        $parentEpisodeList = StepCategoryService::getInstance()->getList(['id' => ['IN', implode(',', array_column($stepCategoryList, 'parent_id', 'parent_id'))]], '*');
        if (empty($parentEpisodeList)) {
            throw new LogicException('support step_category not second level', ErrorCode::DATA_INVALID);
        }
        switch ($parentEpisodeList[0]['code']) {
            case 'episode':
            case 'storyboard':
                // 剧本、分镜
                $res = new EpisodeReportCount();
                break;
            case 'color':
            case 'dub_in':
            case 'sound_mixing':
            case 'clip':
                // 成片
                $res = new CompleteFilmReportCount();
                break;
            case 'design':
            case 'asset':
            case 'level':
                // 概念、资产、关卡
                $res = new EntityReportCount();
                break;
            case 'sequence':
                // 动捕
                $res = new MotionCaptureReportCount();
                break;
            case 'shot':
                switch ($stepCategoryList[0]['code']) {
                    case 'shot_motion':
                        // 镜头动画
                        $res = new ShotMotionReportCount();
                        break 2;
                    default:
                        // 其他的都走实体统计
                        $res = new EntityReportCount();
                }
                break;
            default:
                $res = new EpisodeReportCount();
//                throw new LogicException('unsupported action: ' . $parentEpisodeList[0]['code'], ErrorCode::DATA_INVALID);
        }
        return $res;
    }

    /**
     * 环节列表
     * @param $projectId
     * @param $dateParam
     * @param $onlyAbnormal
     * @return array
     * @throws Exception
     */
    public function stepCategoryList($projectId, $dateParam, $onlyAbnormal)
    {
        $res = ProjectService::getInstance()->getShowStepCategoryGroup();
        $deliveryData = $this->getProjectMonthDeliveryPercentData($projectId, $dateParam, $res);
        foreach ($res as &$item) {
            $itemPercent = 0;
            $havePlan = false;
            $isPlanDone = false;
            foreach ($item['children'] as &$stepCategory) {
                $stepCategory['delivery_percent'] = 0;
                $stepCategory['have_plan'] = false;
                $stepCategory['is_plan_done'] = true;
                if (isset($deliveryData[$stepCategory['id']])) {
                    $stepCategory['delivery_percent'] = $deliveryData[$stepCategory['id']]['percent'];
                    $havePlan = $stepCategory['have_plan'] = $deliveryData[$stepCategory['id']]['have_plan'];
                    $isPlanDone = $stepCategory['is_plan_done'] = $deliveryData[$stepCategory['id']]['is_plan_done'];
                }
                $itemPercent += $stepCategory['delivery_percent'];
            }
            $item['delivery_percent'] = $itemPercent / count($item['children']);
            $item['have_plan'] = $havePlan;
            $item['is_plan_done'] = $isPlanDone;
        }
        if ($onlyAbnormal) {
            $res = array_filter($res, function ($i) {
                return !$i['is_plan_done'];
            });
            $res = array_values($res);
        }
        return $res;
    }

    /**
     * 计算项目月度交付比例数据
     * @param $projectId
     * @param $dateParam
     * @param $stepCategoryGroupList
     * @return array
     */
    private function getProjectMonthDeliveryPercentData($projectId, $dateParam, $stepCategoryGroupList)
    {
        $res = [];
        $ms = !empty($dateParam['month']) ? $dateParam['month'] : $dateParam['start_month'];
        $me = isset($dateParam['end_month']) ? date("Y-m-d H:i:s", strtotime($dateParam['end_month'] . ' +1 month -1 second')) : null;
        foreach ($stepCategoryGroupList as $stepCategoryGroupItem) {
            if (!empty($stepCategoryGroupItem['children_ids'])) {
                $handler = $this->getHandleObject($stepCategoryGroupItem['children_ids']);
                $handler->getData($projectId, $stepCategoryGroupItem['children_ids'], $ms, $me, false);
                foreach ($stepCategoryGroupItem['children_ids'] as $childrenId) {
                    $res[$childrenId] = [
                        'percent' => $handler->getDeliveryPercent(),
                        'have_plan' => $handler->getHavePlan(),
                        'is_plan_done' => $handler->getIsPlanDone(),
                    ];
                }
            }
        }
        return $res;
    }

    /**
     * 报表详情
     * @param $projectId
     * @param $dateParam
     * @param $stepCategoryIdList
     * @return array
     */
    public function detail($projectId, $dateParam, $stepCategoryIdList)
    {
        $handler = $this->getHandleObject($stepCategoryIdList);
        $ms = !empty($dateParam['month']) ? $dateParam['month'] : $dateParam['start_month'];
        $me = isset($dateParam['end_month']) ? date("Y-m-d H:i:s", strtotime($dateParam['end_month'] . ' +1 month -1 second')) : null;
        $handler->getData($projectId, $stepCategoryIdList, $ms, $me, true);

        return [
            'plan_delivery' => $handler->getPlanDelivery(),
            'add_delivery' => $handler->getAddDelivery(),
            'should_delivery' => $handler->getShouldDelivery(),
            'delivery' => $handler->getDelivery(),
            'delivery_unit' => $handler->getDeliveryUnit(),
            'delivery_history' => $handler->getDeliveryHistory(),
            'abnormal' => $handler->getAbnormal(),
            'delivery_percent' => $handler->getDeliveryPercent(),
            'delivery_percent_history' => $handler->getDeliveryPercentHistory(),
            'pass_percent' => $handler->getPassPercent(),
            'pass_percent_history' => $handler->getPassPercentHistory(),
            'task_work_order_percent' => $handler->getTaskWorkOrderPercent(),
            'task_work_order_percent_history' => $handler->getTaskWorkOrderPercentHistory(),
            'have_plan' => $handler->getHavePlan(),
            'is_plan_done' => $handler->getIsPlanDone(),
        ];
    }

    /**
     * 获取原计划交付列表
     * @param $param
     * @return array
     */
    public function getDeliveryList($param)
    {
        $page = $param['page'] ?? [1, C('default_page_total')];
        $projectId = $param['filter']['project_id'];
        $stepCategoryIdList = $param['filter']['step_category_id_list'];
        $isAbnormal = ($param['filter']['is_abnormal'] ?? 'no') == "yes";

        if (isset($param['filter']['month'])) {
            $monthStart = strtotime($param['filter']['month']);
            $monthEnd = strtotime($param['filter']['month'] . " +1 month -1 second");
        }

        if (isset($param['filter']['start_month']) && $param['filter']['end_month']) {
            $monthStart = strtotime($param['filter']['start_month']);
            $monthEnd = strtotime($param['filter']['end_month'] . ' +1 month -1 second');
        }

        if (empty($monthStart) || empty($monthEnd)) {
            return [];
        }

        $planList = model(PlanModel::class)
            ->alias('p')
            ->join('task_relation_tag r_step_category on p.task_id = r_step_category.task_id')
            ->where(['p.project_id' => $projectId])
            ->where(['p.type' => PlanModel::TYPE_STEP_CATEGORY, 'p.scope' => PlanModel::SCOPE_SINGLE])
            ->where([['p.end_time' => ['EGT', $monthStart]], ['p.end_time' => ['ELT', $monthEnd]]])
            ->where(['r_step_category.link_module_code' => ['IN', ['step_category', 'show_step_category']]])
            ->where(['r_step_category.link_id' => ['IN', implode(',', $stepCategoryIdList)]])
            ->field('p.entity_id as episode_id,r_step_category.link_id as step_category_id,p.end_time,p.start_time,p.end_time')
            ->group('p.entity_id,r_step_category.link_id')
            ->cache(APP_DEBUG ? false : C('database.default_query_cache_time'))
            ->select();

        $episodeStepCategoryFilter = [];
        $episodeIds = [];
        $episodePlanEndTimeMap = [];
        $episodePlanStepCategoryEndTimeMap = [];
        foreach ($planList as $planItem) {
            $episodeIds[$planItem['episode_id']] = $planItem['episode_id'];
            if (!isset($episodePlanEndTimeMap[$planItem['episode_id']])) {
                $episodePlanEndTimeMap[$planItem['episode_id']] = $planItem['end_time'];
            } elseif (isset($episodePlanEndTimeMap[$planItem['episode_id']]) && ($planItem['end_time'] > $episodePlanEndTimeMap[$planItem['episode_id']])) { //有最大时间取最大时间
                $episodePlanEndTimeMap[$planItem['episode_id']] = $planItem['end_time'];
            }

            $tmp = ["task.episode_id" => $planItem['episode_id'], 'task.step_category_id' => $planItem['step_category_id']];

            if ($isAbnormal && !empty($planItem['end_time']) && $planItem['end_time'] < time()) {
                $tmp[] = [
                    [
                        'task.end_time' => ['GT', $planItem['end_time']],
                    ],
                    [
                        'task.end_time' => 0,
                    ],
                    '_logic' => 'OR'
                ];

                $episodePlanStepCategoryEndTimeMap[$planItem['episode_id']][$planItem['step_category_id']] = $planItem['end_time'];
            }

            $episodeStepCategoryFilter[] = $tmp;
        }
        $episodeStepCategoryFilter['_logic'] = 'OR';
        $episodeStepCategoryFilter = [$episodeStepCategoryFilter];

        if (empty($episodeIds)) {
            return [];
        }

        $episodeIds = array_values($episodeIds);
        $episodeList = model(EntityModel::class)->where(['id' => ['IN', $episodeIds]])->field("id,code")->select();

        $episodeCodes = [];
        $episodeMap = [];
        foreach ($episodeList as $episodeItem) {
            $episodeCodes[] = $episodeItem['code'];
            $episodeMap[$episodeItem['code']] = $episodeItem['id'];
        }


        $module = $this->dealStepCategoryGetModule($stepCategoryIdList);

        $entityList = [];

        $closedStatus = StatusService::getInstance()->getTaskDefaultClosedStatus();
        $entityFilter = [
            'entity.project_id' => $projectId,
            'entity.module_id' => $module['id'],
            'entity.initial_episode_code' => ["IN", $episodeCodes],
            'task.step_type' => TaskModel::STEP_TYPE_STEP,
            "task.json->>'$.task_status'" => ['NEQ', $closedStatus['id']],
        ];

        if (isset($param['filter']['category'])) {
            $entityFilter["JSON_UNQUOTE(JSON_EXTRACT(entity.json,CONCAT('$.',module.code,'_category')))"] = $param['filter']['category'];
        }

        if (isset($param['filter']['grade'])) {
            $entityFilter["JSON_UNQUOTE(JSON_EXTRACT(entity.json,CONCAT('$.',module.code,'_grade')))"] = $param['filter']['grade'];
        }

        if (isset($param['filter']['name'])) {
            $entityFilter["entity.name"] = $param['filter']['name'];
        }

        if (isset($param['filter']['executor'])) {
            $entityFilter['task.executor'] = $param['filter']['executor'];
        }

        if (isset($param['filter']['is_append'])) {
            $entityFilter['entity.is_append'] = $param['filter']['is_append'];
        }

        switch ($module['code']) {
            case 'design':
            case 'asset':
            case 'level':
            case 'sequence':
            case 'shot':
                if ($isAbnormal) {
                    // 概念、资产、关卡
                    $entityList = model(EntityModel::class)
                        ->join("module on entity.module_id = module.id")
                        ->join("task on entity.id = task.entity_id")
                        ->where($entityFilter)
                        ->where($episodeStepCategoryFilter)
                        ->field([
                            "task.id",
                            "task.step_category_id",
                            "task.episode_id",
                            "entity.name",
                            "entity.initial_episode_code",
                            "entity.showings_number",
                            "task.name as task_name",
                            "task.end_time",
                            "task.executor",
                            "JSON_UNQUOTE(JSON_EXTRACT(entity.json,CONCAT('$.',module.code,'_category'))) as category",
                            "JSON_UNQUOTE(JSON_EXTRACT(entity.json,CONCAT('$.',module.code,'_grade'))) as grade",
                        ])
                        ->page(...$page)
                        ->select();

                    if (empty($entityList)) {
                        return [];
                    }

                    $executor = array_column($entityList, 'executor', 'executor');
                    unset($executor[0]);
                    $userList = [];
                    if (!empty($executor)) {
                        $userList = model(UserModel::class)->where(['id' => ['IN', $executor]])->field("id,name,avatar")->select();
                        $userList = array_column($userList, null, 'id');
                    }

                    $entityList = array_map(function ($entityItem) use ($userList, $episodePlanStepCategoryEndTimeMap) {
                        $overdueDay = DateInterval::createFromDateString("-0 day");
                        $overdue = 0;
                        if (!empty($episodePlanStepCategoryEndTimeMap[$entityItem['episode_id']][$entityItem['step_category_id']])) {
                            $planTime = $episodePlanStepCategoryEndTimeMap[$entityItem['episode_id']][$entityItem['step_category_id']];
                            if ($planTime < $entityItem['end_time']) {
                                $startTime = date('Y-m-d', $planTime);
                                $endTime = date('Y-m-d', $entityItem['end_time']);
                                $overdueDay = date_diff(date_create($startTime), date_create($endTime));
                                $overdue = $entityItem['end_time'] - $planTime;
                            } else {
                                $overdue = time() - $planTime;
                            }
                        }
                        $entityItem['abnormal_info'] = [
                            'overdue_day' => $overdueDay->days ?? 0, // 逾期天数
                            'overdue' => $overdue,
                        ];
                        $entityItem['executor'] = $userList[$entityItem['executor']] ?? null;
                        return $entityItem;
                    }, $entityList);
                } else {
                    // 概念、资产、关卡
                    $entityList = model(EntityModel::class)
                        ->join("module on entity.module_id = module.id")
                        ->join("task on entity.id = task.entity_id")
                        ->where($entityFilter)
                        ->where($episodeStepCategoryFilter)
                        ->field([
                            "entity.id",
                            "entity.name",
                            "entity.initial_episode_code",
                            "entity.showings_number",
                            "JSON_UNQUOTE(JSON_EXTRACT(entity.json,CONCAT('$.',module.code,'_category'))) as category",
                            "JSON_UNQUOTE(JSON_EXTRACT(entity.json,CONCAT('$.',module.code,'_grade'))) as grade",
                        ])
                        ->group("entity.id")
                        ->page(...$page)
                        ->select();

                    //补充计划交付时间
                    $entityList = array_map(function ($entityItem) use ($episodePlanEndTimeMap, $episodeMap) {
                        $entityItem['delivery_time'] = 0;
                        if (isset($episodeMap[$entityItem['initial_episode_code']]) && isset($episodePlanEndTimeMap[$episodeMap[$entityItem['initial_episode_code']]])) {
                            $entityItem['delivery_time'] = $episodePlanEndTimeMap[$episodeMap[$entityItem['initial_episode_code']]];
                        }
                        return $entityItem;
                    }, $entityList);
                }
                break;
        }
        return $entityList;
    }

    /**
     * 通过分类获取模块
     * @param $stepCategoryIdList
     * @return array
     */
    public function dealStepCategoryGetModule($stepCategoryIdList)
    {
        $stepCategoryList = StepCategoryService::getInstance()->getList(['id' => ['IN', implode(',', $stepCategoryIdList)]], '*');
        if (empty($stepCategoryList)) {
            throw new LogicException('step_category not found', ErrorCode::STEP_CATEGORY_NOT_FOUND);
        }
        $parentEpisodeList = StepCategoryService::getInstance()->getList(['id' => ['IN', implode(',', array_column($stepCategoryList, 'parent_id', 'parent_id'))]], '*');
        if (empty($parentEpisodeList)) {
            throw new LogicException('support step_category not second level', ErrorCode::DATA_INVALID);
        }

        return module_by_id($parentEpisodeList[0]['entity_id']);
    }

    /**
     * 获取工单问题列表
     * @param $param
     * @return array
     */
    public function getWorkOrderProblemList($param)
    {
        $page = $param['page'] ?? [1, C('default_page_total')];
        $projectId = $param['filter']['project_id'];
        $stepCategoryIdList = $param['filter']['step_category_id_list'];

        if (isset($param['filter']['month'])) {
            $monthStart = strtotime($param['filter']['month']);
            $monthEnd = strtotime($param['filter']['month'] . " +1 month -1 second");
        }

        if (isset($param['filter']['start_month']) && $param['filter']['end_month']) {
            $monthStart = strtotime($param['filter']['start_month']);
            $monthEnd = strtotime($param['filter']['end_month'] . ' +1 month -1 second');
        }

        if (empty($monthStart) || empty($monthEnd)) {
            return [];
        }

        unset($param['filter']['project_id'], $param['filter']['step_category_id_list'], $param['filter']['month'], $param['filter']['start_month'], $param['filter']['end_month']);

        $taskWorkOrderFilter = $param['filter'];
        $filterProcessTime = $param['process_time_filter'] ?? null;
        if (!empty($filterProcessTime)) {
            TaskWorkOrderService::getInstance()->parseProcessTimeFilter($taskWorkOrderFilter, $filterProcessTime);
        }

        if (empty($taskWorkOrderFilter)) {
            $taskWorkOrderIds = model(TaskWorkOrderModel::class)
                ->where(['task_work_order.project_id' => $projectId, 'task_work_order.step_category_id' => ["IN", $stepCategoryIdList], 'task_work_order.created' => ['BETWEEN', [$monthStart, $monthEnd]]])
                ->group('task_work_order.id')
                ->field('task_work_order.id')
                ->page(...$page)
                ->select();
        } else {
            $isEntityFilter = false;
            $isTaskFilter = false;
            foreach ($taskWorkOrderFilter as $taskWorkOrderFilterKey => $taskWorkOrderFilterItem) {
                if ($isTaskFilter === false && strpos($taskWorkOrderFilterKey, "task.") !== false) {
                    $isTaskFilter = true;
                }
                if ($isEntityFilter === false && strpos($taskWorkOrderFilterKey, "entity.") !== false) {
                    $isEntityFilter = $isTaskFilter = true;
                }
            }
            $taskWorkOrderQuery = model(TaskWorkOrderModel::class)
                ->join('task on task.id = task_work_order.task_id');

            if ($isEntityFilter) {
                $taskWorkOrderQuery = $taskWorkOrderQuery->join("entity on entity.id = task.entity_id");
            }
            $taskWorkOrderIds = $taskWorkOrderQuery
                ->where(['task_work_order.project_id' => $projectId, 'task_work_order.step_category_id' => ["IN", $stepCategoryIdList], 'task_work_order.created' => ['BETWEEN', [$monthStart, $monthEnd]]])
                ->where($taskWorkOrderFilter)
                ->group('task_work_order.id')
                ->field('task_work_order.id')
                ->page(...$page)
                ->select();
        }
        if (empty($taskWorkOrderIds)) {
            return [];
        }
        $taskWorkOrderIds = array_column($taskWorkOrderIds, 'id');
        $taskWorkOrderFields = [
            "task_work_order.id", "task_work_order.number", "task_work_order.task_id", "task_work_order.status", "task_work_order.created", "task_work_order.report_type", "task_work_order.close_at"
        ];
        $taskWorkOrderList = model(TaskWorkOrderModel::class)
            ->where(['id' => ["IN", $taskWorkOrderIds]])
            ->field($taskWorkOrderFields)
            ->select();
        if (empty($taskWorkOrderList)) {
            return [];
        }
        $taskIds = array_column($taskWorkOrderList, 'task_id', 'task_id');
        $taskList = model(TaskModel::class)
            ->join("entity on entity.id = task.entity_id")
            ->join("module on module.id = entity.module_id")
            ->where(['task.id' => ["IN", join(",", $taskIds)]])
            ->field([
                'task.id',
                'entity.initial_episode_code',
                'entity.name',
                'entity.showings_number',
                'task.name as task_name',
                "JSON_UNQUOTE(JSON_EXTRACT(entity.json,CONCAT('$.',module.code,'_category'))) as category",
                "JSON_UNQUOTE(JSON_EXTRACT(entity.json,CONCAT('$.',module.code,'_grade'))) as grade"
            ])
            ->select();
        $taskList = array_column($taskList, null, 'id');
        return array_map(function ($taskWorkOrderItem) use ($taskList) {
            $taskWorkOrderItem['entity'] = $taskList[$taskWorkOrderItem['task_id']] ?? null;
            return $taskWorkOrderItem;
        }, $taskWorkOrderList);
    }

    /**
     * 获取环节工单统计
     * @param $param
     * @return array
     */
    public function getWorkOrderProblemCount($param)
    {
        $projectId = $param['filter']['project_id'];
        $stepCategoryIdList = $param['filter']['step_category_id_list'];

        if (isset($param['filter']['month'])) {
            $monthStart = strtotime($param['filter']['month']);
            $monthEnd = strtotime($param['filter']['month'] . " +1 month -1 second");
        }

        if (isset($param['filter']['start_month']) && $param['filter']['end_month']) {
            $monthStart = strtotime($param['filter']['start_month']);
            $monthEnd = strtotime($param['filter']['end_month'] . ' +1 month -1 second');
        }

        if (empty($monthStart) || empty($monthEnd)) {
            return [];
        }
        unset($param['filter']['step_category_id_list'], $param['filter']['month'], $param['filter']['end_month']);
        unset($param['filter']['project_id'], $param['filter']['start_month']);

        // 处理时长
        $taskWorkOrderFilter = [];
        $filterProcessTime = $param['process_time_filter'] ?? null;
        if (!empty($filterProcessTime)) {
            TaskWorkOrderService::getInstance()->parseProcessTimeFilter($taskWorkOrderFilter, $filterProcessTime);
        }

        if (isset($param['filter']['deal_time'])) {
            $taskWorkOrderFilter[] = [
                'task_work_order.close_at' => ['GT', 0],
                '(task_work_order.close_at - task_work_order.created)' => $param['filter']['deal_time']
            ];
            unset($param['filter']['deal_time']);
        }

        $taskWorkOrderType = OptionsService::getInstance()->getOptionsConfigItemData("task_work_order_config", "type");
        $taskWorkOrderTypeList = explode(",", $taskWorkOrderType);

        $taskWorkOrderCount = model(TaskWorkOrderModel::class)
            ->join("task on task.id = task_work_order.task_id")
            ->join("entity on entity.id = task.entity_id")
            ->where($taskWorkOrderFilter)
            ->where($param['filter'])
            ->where(['task_work_order.project_id' => $projectId, 'task_work_order.step_category_id' => ["IN", $stepCategoryIdList], 'task_work_order.created' => ['BETWEEN', [$monthStart, $monthEnd]]])
            ->field("task_work_order.report_type,count(task_work_order.id) as count")
            ->group("task_work_order.report_type")
            ->cache(APP_DEBUG ? false : C('database.default_query_cache_time'))
            ->select();
        $taskWorkOrderCount = array_column($taskWorkOrderCount, 'count', 'report_type');

        // 补充默认的类型的
        $countList = [];
        foreach ($taskWorkOrderTypeList as $taskWorkOrderTypeItem) {
            $countList[] = ['report_type' => $taskWorkOrderTypeItem, 'count' => $taskWorkOrderCount[$taskWorkOrderTypeItem] ?? 0];
        }

        return $countList;

    }
}
